package com.swak.algs.sample;

import java.util.Set;
import java.util.function.Supplier;

import com.swak.algs.array.Compares;
import com.swak.algs.array.Copys;
import com.swak.algs.array.Prints;
import com.swak.algs.array.Swaps;
import com.swak.utils.Sets;

/**
 * 样本
 * 
 * @author lifeng
 */
public class Samples {

	/**
	 * 百分之几的概率， 例如 99.99% 的概率则传入 99.99
	 * 
	 * @param percent
	 * @return
	 */
	public static boolean probability(double percent) {
		double len = (double) (Math.random() * (100)) + 1; // 1 ~ 100
		if (len < percent) {
			return true;
		}
		return false;
	}

	/**
	 * 返回一个随机的样本
	 * 
	 * @param maxValue  每个数小于等于最大值
	 * @param maxlength 小于等于最大的长度
	 * @return
	 */
	public static int[] randomSample(int maxValue, int maxlength) {
		int len = (int) (Math.random() * (maxlength + 1));
		int[] ans = new int[len];
		for (int i = 0; i <= ans.length - 1; i++) {
			ans[i] = Samples.randomNum(maxValue);
		}
		return ans;
	}

	/**
	 * 返回一个随机的样本, 有序
	 * 
	 * @param maxValue  每个数小于等于最大值
	 * @param maxlength 小于等于最大的长度
	 * @return
	 */
	public static int[] orderRandomSample(int maxValue, int maxlength) {
		int len = (int) (Math.random() * (maxlength + 1));
		int[] ans = new int[len];
		ans[0] = Samples.randomNum(maxValue);
		for (int i = 1; i <= ans.length - 1; i++) {
			do {
				ans[i] = Samples.randomNum(maxValue);
			} while (ans[i] >= ans[i - 1]);
		}
		return ans;
	}

	/**
	 * 返回一个随机的样本, 局部最小
	 * 
	 * @param maxValue  每个数小于等于最大值
	 * @param maxlength 小于等于最大的长度
	 * @return
	 */
	public static int[] lmRandomSample(int maxValue, int maxlength) {
		int len = (int) (Math.random() * (maxlength + 1));
		int[] ans = new int[len];
		ans[0] = Samples.randomNum(maxValue);
		for (int i = 1; i <= ans.length - 1; i++) {
			do {
				ans[i] = Samples.randomNum(maxValue);
			} while (ans[i] == ans[i - 1]);
		}
		return ans;
	}

	private static int randomNum(int max) {
		return (int) (Math.random() * (max + 1)) - (int) (Math.random() * (max + 1));
	}

	/**
	 * 返回一个固定的样本（中序）
	 * 
	 * @param maxValue 每个数小于等于最大值
	 * @param len      长度
	 * @return
	 */
	public static int[] fixSample() {
		return new int[] { 4, 2, 5, 1, 6, 3, 7 };
	}

	/**
	 * 一种数有K个，其他数有 M 个，且 0 < K < M
	 * 
	 * @param kMax     为 K 的数的大小
	 * @param mMax     为 M 的数的大小
	 * @param maxKinds 数的总类
	 * @param max      最大值
	 * @return
	 */
	public static int[] kAndMKindsRandomSample(int kMax, int mMax, int maxKinds, int max) {
		int a = (int) (Math.random() * kMax) + 1;
		int b = (int) (Math.random() * mMax) + 1;
		int k = Math.min(a, b);
		int m = Math.max(a, b) + 2;
		int kinds = (int) (Math.random() * maxKinds) + 2;

		int[] ans = new int[k + kinds * m];

		/*
		 * 为 K 的数
		 */
		int one = Samples.randomNum(max);
		Set<Integer> sets = Sets.newHashSet();
		sets.add(one);
		int index = 0;
		for (; index < k; index++) {
			ans[index] = one;
		}

		/*
		 * 其他数
		 */
		while (kinds > 0) {
			do {
				one = Samples.randomNum(max);
			} while (sets.contains(one));

			for (int i = 0; i < m; i++) {
				ans[index++] = one;
			}
			kinds--;
		}

		/*
		 * 打乱
		 */
		for (int i = 0; i < ans.length; i++) {
			int j = (int) (Math.random() * ans.length) % ans.length;
			Swaps.swap(ans, i, j);
		}
		return ans;
	}

	/**
	 * 固定长度的样本的测试
	 * 
	 * @param maxValue
	 * @param len
	 * @param times
	 * @param cases
	 * @return
	 */
	public static int[] randomSampleTest(int maxValue, int maxlength, int times, Case... cases) {
		return Samples.doSampleTest(() -> Samples.randomSample(maxValue, maxlength), times, cases);
	}

	/**
	 * 固定长度的样本的测试
	 * 
	 * @param maxValue
	 * @param len
	 * @param times
	 * @param cases
	 * @return
	 */
	public static int[] orderSampleTest(int maxValue, int maxlength, int times, Case... cases) {
		return Samples.doSampleTest(() -> Samples.orderRandomSample(maxValue, maxlength), times, cases);
	}

	/**
	 * 固定长度的样本的测试
	 * 
	 * @param maxValue
	 * @param len
	 * @param times
	 * @param cases
	 * @return
	 */
	public static int[] lmSampleTest(int maxValue, int maxlength, int times, Case... cases) {
		return Samples.doSampleTest(() -> Samples.lmRandomSample(maxValue, maxlength), times, cases);
	}

	private static int[] doSampleTest(Supplier<int[]> supplier, int times, Case... cases) {
		long start = System.currentTimeMillis();
		System.out.println("Begin.");
		for (int i = 0; i < times; i++) {
			int[] ans = supplier.get();
			int[][] anss = new int[cases.length][];
			for (int j = 0; j < cases.length; j++) {
				int[] caAns = Copys.copy(ans);
				cases[j].test(caAns);
				anss[j] = caAns;
			}
			if (!Compares.isEquals(anss)) {
				System.out.println("Error.");
				Prints.print(ans);
				for (int j = 0; j < anss.length; j++) {
					Prints.print(anss[j]);
				}
				return ans;
			}
		}
		System.out.println("Nice, Use " + ((System.currentTimeMillis() - start) / 1000) + "s.");
		return null;
	}

	@FunctionalInterface
	public static interface Case {
		void test(int[] ans);
	}
}